package com.zycfc.zsf.boot.mybatis.dynamicdatasource.interceptor;

import org.apache.ibatis.executor.*;
import org.apache.ibatis.cache.*;
import java.util.regex.*;
import com.zycfc.zsf.boot.common.context.*;
import org.springframework.transaction.support.*;
import com.zycfc.zsf.boot.util.bean.*;
import com.zycfc.zsf.boot.util.text.*;
import com.zycfc.zsf.boot.util.time.*;
import org.apache.ibatis.mapping.*;
import com.zycfc.zsf.boot.mybatis.dynamicdatasource.*;
import org.apache.ibatis.session.*;
import org.apache.ibatis.executor.statement.*;
import com.zycfc.zsf.boot.util.json.*;
import java.lang.reflect.*;
import com.zycfc.zsf.boot.logback.util.*;
import com.zycfc.zsf.boot.logback.core.*;
import org.apache.ibatis.plugin.*;
import java.util.*;
import org.slf4j.*;

@Intercepts({ @Signature(type = Executor.class, method = "update", args = { MappedStatement.class, Object.class }), @Signature(type = Executor.class, method = "query", args = { MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class }) })
public class DynamicDatasourceInterceptor implements Interceptor
{
    private static final Logger log;
    private static final String REGEX = ".*insert\\u0020.*|.*delete\\u0020.*|.*update\\u0020.*";
    private static final String BLANK_STR = "";
    private static final String EMPTY_PARAM_STR = "{}";
    Pattern insertPattern;
    Pattern updatePattern;
    
    public DynamicDatasourceInterceptor() {
        this.insertPattern = Pattern.compile("\\s*insert", 34);
        this.updatePattern = Pattern.compile("\\s*update", 34);
    }
    
    public Object intercept(final Invocation invocation) throws Throwable {
        TraceUtil.beginApplicationNameAndEnv();
        if (StringUtils.isEmpty(TraceUtil.getZycfcTrace())) {
            TraceUtil.beginZycfcTrace(ZsfRootContext.get(TraceUtil.getZycfcTraceKeyName()));
        }
        final long start = System.currentTimeMillis();
        final boolean synchronizationActive = TransactionSynchronizationManager.isSynchronizationActive();
        final boolean isActualTransactionActive = TransactionSynchronizationManager.isActualTransactionActive();
        final boolean isCurrentTransactionReadOnly = TransactionSynchronizationManager.isCurrentTransactionReadOnly();
        String dynamicDataSourceGlobal = DataSourceContextHolder.get();
        final String methodName = invocation.getMethod().getName();
        final Object target = invocation.getTarget();
        final Object[] objects = invocation.getArgs();
        final Object sqlParamObject = objects[1];
        final MappedStatement mappedStatement = (MappedStatement)objects[0];
        final Configuration configuration = mappedStatement.getConfiguration();
        final StatementHandler handler = configuration.newStatementHandler((Executor)target, mappedStatement, sqlParamObject, RowBounds.DEFAULT, (ResultHandler)null, (BoundSql)null);
        final BoundSql boundSql = handler.getBoundSql();
        String sql = boundSql.getSql();
        if ("update".equals(methodName)) {
            if (this.isInsert(sql)) {
                if (BeanUtils.propertyExists(sqlParamObject, "id") && StringUtils.isEmpty(BeanUtils.getProperty(sqlParamObject, "id"))) {
                    BeanUtils.setPropertyLombok(sqlParamObject, "id", (Object)UuidUtils.getUuid());
                }
                if (BeanUtils.propertyExists(sqlParamObject, "createTime") && StringUtils.isEmpty(BeanUtils.getProperty(sqlParamObject, "createTime"))) {
                    BeanUtils.setPropertyLombok(sqlParamObject, "createTime", (Object)DateUtils.getCurTimestamp());
                }
                if (BeanUtils.propertyExists(sqlParamObject, "updateTime") && StringUtils.isEmpty(BeanUtils.getProperty(sqlParamObject, "updateTime"))) {
                    BeanUtils.setPropertyLombok(sqlParamObject, "updateTime", (Object)DateUtils.getCurTimestamp());
                }
            }
            else if (this.isUpdate(sql) && BeanUtils.propertyExists(sqlParamObject, "updateTime") && StringUtils.isEmpty(BeanUtils.getProperty(sqlParamObject, "updateTime"))) {
                BeanUtils.setPropertyLombok(sqlParamObject, "updateTime", (Object)DateUtils.getCurTimestamp());
            }
        }
        if (!isCurrentTransactionReadOnly) {
            boolean clearDataSource = false;
            if (null == dynamicDataSourceGlobal) {
                if ("update".equals(methodName)) {
                    DataSourceContextHolder.write();
                }
                else {
                    DataSourceContextHolder.read();
                }
                dynamicDataSourceGlobal = DataSourceContextHolder.get();
                clearDataSource = true;
            }
            if (clearDataSource) {
                DataSourceContextHolder.clear();
            }
        }
        else {
            if (mappedStatement.getSqlCommandType().equals((Object)SqlCommandType.SELECT)) {
                if (mappedStatement.getId().contains("!selectKey")) {
                    dynamicDataSourceGlobal = DataSourceType.write.getType();
                }
                else {
                    sql = boundSql.getSql().toLowerCase(Locale.CHINA).replaceAll("[\\t\\n\\r]", " ");
                    if (sql.matches(".*insert\\u0020.*|.*delete\\u0020.*|.*update\\u0020.*")) {
                        dynamicDataSourceGlobal = DataSourceType.write.getType();
                    }
                    else {
                        dynamicDataSourceGlobal = DataSourceType.read.getType();
                    }
                }
            }
            else {
                dynamicDataSourceGlobal = DataSourceType.read.getType();
            }
            DataSourceContextHolder.put(dynamicDataSourceGlobal);
            DataSourceContextHolder.clear();
        }
        return this.proceed(invocation, dynamicDataSourceGlobal, start, mappedStatement, sql, sqlParamObject, isCurrentTransactionReadOnly, synchronizationActive, methodName, isActualTransactionActive);
    }
    
    private String getParameters(final Object sqlParamObject) {
        try {
            final String sqlParamStr = JsonUtils.jacksonObjectToJson(sqlParamObject);
            if ("{}".equals(sqlParamStr)) {
                return "";
            }
            return (sqlParamStr.length() <= 800) ? sqlParamStr : sqlParamStr.substring(0, 800);
        }
        catch (Throwable e) {
            return (e != null) ? e.getMessage() : null;
        }
    }
    
    private Object proceed(final Invocation invocation, final String dynamicDataSourceGlobal, final long start, final MappedStatement mappedStatement, final String sql, final Object sqlParamObject, final boolean isCurrentTransactionReadOnly, final boolean synchronizationActive, final String methodName, final boolean isActualTransactionActive) throws InvocationTargetException, IllegalAccessException {
        String exeSts = "F";
        Object result = null;
        String respCode = "0000000";
        String respMsg = "\u64cd\u4f5c\u6210\u529f";
        try {
            result = invocation.proceed();
            exeSts = "S";
        }
        catch (InvocationTargetException e) {
            respMsg = e.getMessage();
            if (StringUtils.isEmpty(respMsg) && e.getTargetException() != null) {
                respMsg = e.getTargetException().getMessage();
            }
            respCode = respMsg;
            throw e;
        }
        catch (IllegalAccessException e2) {
            respMsg = (respCode = e2.getMessage());
            throw e2;
        }
        finally {
            try {
                final int totalCount = this.getTotalCount(result);
                final String parametersStr = (sqlParamObject != null) ? this.getParameters(sqlParamObject) : "";
                final Long exeTm = System.currentTimeMillis() - start;
                DynamicDatasourceInterceptor.log.info("SqlId:[{}], Strategy:{}, ExeTime:{}ms, IsCurrentTransactionReadOnly:{}, SynchronizationActive:{}, MethodName:{}, IsActualTransactionActive:{}, Total:{}, Parameters:{}", new Object[] { mappedStatement.getId(), dynamicDataSourceGlobal, exeTm, isCurrentTransactionReadOnly, synchronizationActive, methodName, isActualTransactionActive, totalCount, parametersStr });
                final Long pointStartTime = System.currentTimeMillis();
                final SqlLogInf sqlLogInf = new SqlLogInfBuilder().setDatasourceType(dynamicDataSourceGlobal).setExeTm((long)exeTm).setSqlCommandType(mappedStatement.getSqlCommandType().name()).setSql(sql).setSqlId(mappedStatement.getId()).setSqlParam((Object)parametersStr).setExeSts(exeSts).setRespCode(respCode).setRespMsg(respMsg).setTotalCount(Integer.valueOf(totalCount)).build();
                LogUtil.printSqlLog(sqlLogInf);
                final Long pointEndTime = System.currentTimeMillis() - pointStartTime;
                if (pointEndTime > 5000L) {
                    DynamicDatasourceInterceptor.log.info("TempTimePrint-{}", (Object)exeTm);
                }
            }
            catch (Throwable e3) {
                DynamicDatasourceInterceptor.log.error("printSqlLog-Exception:", e3);
            }
        }
        return result;
    }
    
    private int getTotalCount(final Object result) {
        if (result == null) {
            return 0;
        }
        try {
            if (result instanceof Collection) {
                return ((Collection)result).size();
            }
            if (result instanceof Integer) {
                return (int)result;
            }
        }
        catch (Throwable e) {
            DynamicDatasourceInterceptor.log.error("getTotalCount-Exception:", e);
        }
        return 1;
    }
    
    public Object plugin(final Object target) {
        if (target instanceof Executor) {
            return Plugin.wrap(target, (Interceptor)this);
        }
        return target;
    }
    
    public void setProperties(final Properties properties) {
    }
    
    protected boolean isInsert(final String sql) {
        return this.insertPattern.matcher(sql).find();
    }
    
    protected boolean isUpdate(final String sql) {
        return this.updatePattern.matcher(sql).find();
    }
    
    static {
        log = LoggerFactory.getLogger((Class)DynamicDatasourceInterceptor.class);
    }
}
